import {useRef, useState, cloneElement, useEffect} from 'react';

import styles from './cubox.module.css';

const contPoint = (x, y, inc, dir) => {
  const [w1, h1, w2, h2] = {
    TL: [0, -inc, -inc, 0],
    TR: [inc, 0, 0, -inc],
    BR: [0, inc, inc, 0],
    BL: [-inc, 0, 0, inc]
  }[dir];
  return `${x + w1} ${y + h1} ${x + w2} ${y + h2}`
}

const pathString = (size, radius, rawStrokeWidth=0, corners=['C', 'C', 'C', 'C']) => {

  const {height: h, width: w} = size;
  
  const {max, min} = Math;
	
  const radi = max(min(radius, min(w, h) / 2), 5);

	const off = min(radi, rawStrokeWidth);

	const inc = min( radi / off , off) / 2;

  const [TL, TR, BR, BL] = corners;

  return (
   `M ${off} ${radi}
    ${TL === 'C' ? `C ${contPoint(off, off, inc, 'TL')} ${radi} ${off}` : `L ${off} ${off} L ${radi} ${off}`}
    L ${w - radi} ${off}
    ${TR === 'C' ? `C ${contPoint(w - off, off, inc, 'TR')} ${w - off} ${radi}` : `L ${w - off} ${off} L ${w - off} ${radi}`}
    L ${w - off} ${h - radi}
    ${BR === 'C' ? `C ${contPoint(w - off, h - off, inc, 'BR')} ${w - radi} ${h - off}` : `L ${w - off} ${h - off} L ${w - radi} ${h -off}`}
    L ${radi} ${h - off}
    ${BL === 'C' ? `C ${contPoint(off, h - off, inc, 'BL')} ${off} ${h - radi}` : `L ${off} ${h - off} L ${off} ${h - radi}`}
    L ${off} ${radi}`
  )
}

const Cubox = (props) => {

	const {
		cornerRadius=20,
		strokeWidth=1,

		isOutlined=false,

		backColor='black',
		foreColor='white',
    opacity=1,
    style,

    corners=['C', 'C', 'C', 'C'],
		children,

    onClick,

    ...remProps
	} = props;

  if (Array.isArray(children)) {
    throw Error('Cubox contain single <div /> only. Wrap your text or list with a <div />.')
  }

  const obs = useRef(null)
  const ref = useRef(null)

  const [size, setSize] = useState({height: 20, width: 20});

  useEffect(() => {
    if (ref.current) {
      const {height, width} = ref.current.getBoundingClientRect();

      obs.current = new ResizeObserver(([{borderBoxSize:[{blockSize, inlineSize}]}]) => {
        setSize({height: blockSize, width: inlineSize});
      });
  
      obs.current.observe(ref.current, {box: 'border-box'});

      setSize({height, width})
    };

    return () => {
      obs.current && obs.current.disconnect();
    }
  }, [ref])

  const frame = isOutlined
  ? <>
      <path style={{fill:backColor, fillOpacity:opacity}} d={pathString(size, cornerRadius, 0, corners)}/>
      <path style={{fill:foreColor}} d={pathString(size, cornerRadius, strokeWidth, corners)}/>
    </>
  : <path style={{fill:backColor, fillOpacity:opacity}} d={pathString(size, cornerRadius, 0, corners)}/>

  // basic inline style for being overlaid by children
  const backProps = {
    viewBox: `0 0 ${size.width} ${size.height}`,
    style: {
      position:'absolute',
      top:0,
      left:0,
      ...size,
    }
  }
  return <div className={styles['cubox']} style={style} onClick={onClick}>
    <svg {...backProps}>{frame}</svg>
    <div className={styles['cubox-inner']} {...remProps}>
      {cloneElement(children, {ref})}
    </div>
  </div>
}

export default Cubox;